package service

import (
	"context"
	"fmt"
	"time"

	"go.uber.org/zap"

	"yanxue_ai_go/pkg/common/database"
	"yanxue_ai_go/pkg/common/logger"
	"yanxue_ai_go/services/rag/internal/models"
)

// RAGService RAG服务
type RAGService struct {
	embeddingService *EmbeddingService
	searchService    *SearchService
	qaService        *QAService
}

// NewRAGService 创建RAG服务
func NewRAGService() *RAGService {
	return &RAGService{
		embeddingService: NewEmbeddingService(),
		searchService:    NewSearchService(),
		qaService:        NewQAService(),
	}
}

// CreateKnowledgeBase 创建知识库
func (rs *RAGService) CreateKnowledgeBase(ctx context.Context, req *CreateKnowledgeBaseRequest) (*models.KnowledgeBase, error) {
	kb := &models.KnowledgeBase{
		Name:        req.Name,
		Description: req.Description,
		Type:        req.Type,
		Config:      req.Config,
		Status:      "active",
		IndexName:   fmt.Sprintf("kb_%s_%d", req.Name, time.Now().Unix()),
		Collection:  fmt.Sprintf("kb_%s_%d", req.Name, time.Now().Unix()),
		CreatedBy:   req.CreatedBy,
	}

	if err := database.DB.Create(kb).Error; err != nil {
		logger.Logger.Error("Failed to create knowledge base", zap.Error(err))
		return nil, fmt.Errorf("failed to create knowledge base: %w", err)
	}

	// 创建Elasticsearch索引和Milvus集合
	if err := rs.searchService.CreateIndex(ctx, kb.IndexName); err != nil {
		logger.Logger.Error("Failed to create search index", zap.Error(err))
	}

	if err := rs.searchService.CreateCollection(ctx, kb.Collection); err != nil {
		logger.Logger.Error("Failed to create vector collection", zap.Error(err))
	}

	logger.Logger.Info("Knowledge base created successfully", zap.Uint("kb_id", kb.ID))
	return kb, nil
}

// GetKnowledgeBase 获取知识库
func (rs *RAGService) GetKnowledgeBase(ctx context.Context, id uint) (*models.KnowledgeBase, error) {
	var kb models.KnowledgeBase
	if err := database.DB.Preload("Documents").First(&kb, id).Error; err != nil {
		return nil, fmt.Errorf("knowledge base not found: %w", err)
	}
	return &kb, nil
}

// ListKnowledgeBases 获取知识库列表
func (rs *RAGService) ListKnowledgeBases(ctx context.Context, req *ListKnowledgeBasesRequest) ([]*models.KnowledgeBase, int64, error) {
	var kbs []*models.KnowledgeBase
	var total int64

	query := database.DB.Model(&models.KnowledgeBase{})

	if req.Type != "" {
		query = query.Where("type = ?", req.Type)
	}
	if req.Status != "" {
		query = query.Where("status = ?", req.Status)
	}

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, fmt.Errorf("failed to count knowledge bases: %w", err)
	}

	offset := (req.Page - 1) * req.PageSize
	if err := query.Offset(offset).Limit(req.PageSize).Find(&kbs).Error; err != nil {
		return nil, 0, fmt.Errorf("failed to list knowledge bases: %w", err)
	}

	return kbs, total, nil
}

// CreateDocument 创建文档
func (rs *RAGService) CreateDocument(ctx context.Context, req *CreateDocumentRequest) (*models.Document, error) {
	doc := &models.Document{
		KnowledgeBaseID: req.KnowledgeBaseID,
		Title:           req.Title,
		Content:         req.Content,
		ContentType:     req.ContentType,
		Source:          req.Source,
		Language:        req.Language,
		Size:            int64(len(req.Content)),
		WordCount:       rs.countWords(req.Content),
		Status:          "pending",
		Metadata:        req.Metadata,
		EmbeddingModel:  "text-embedding-ada-002", // 默认模型
		CreatedBy:       req.CreatedBy,
	}

	if err := database.DB.Create(doc).Error; err != nil {
		logger.Logger.Error("Failed to create document", zap.Error(err))
		return nil, fmt.Errorf("failed to create document: %w", err)
	}

	// 异步处理文档（分块、向量化）
	go rs.processDocument(ctx, doc)

	logger.Logger.Info("Document created successfully", zap.Uint("doc_id", doc.ID))
	return doc, nil
}

// processDocument 异步处理文档
func (rs *RAGService) processDocument(ctx context.Context, doc *models.Document) {
	// 更新状态为处理中
	database.DB.Model(doc).Update("status", "processing")

	// 文档分块
	chunks, err := rs.chunkDocument(doc.Content, 1000) // 默认1000字符一块
	if err != nil {
		logger.Logger.Error("Failed to chunk document", zap.Error(err))
		database.DB.Model(doc).Updates(map[string]interface{}{
			"status":            "failed",
			"processing_log":    fmt.Sprintf("Chunk error: %v", err),
			"last_processed_at": time.Now(),
		})
		return
	}

	// 保存分块并向量化
	for i, chunkContent := range chunks {
		chunk := &models.Chunk{
			DocumentID: doc.ID,
			Content:    chunkContent,
			ChunkIndex: i,
			Size:       len(chunkContent),
			WordCount:  rs.countWords(chunkContent),
		}

		if err := database.DB.Create(chunk).Error; err != nil {
			logger.Logger.Error("Failed to save chunk", zap.Error(err))
			continue
		}

		// 生成向量并存储到Milvus
		if err := rs.embeddingService.EmbedAndStore(ctx, chunk); err != nil {
			logger.Logger.Error("Failed to embed chunk", zap.Error(err))
		}
	}

	// 更新文档状态
	database.DB.Model(doc).Updates(map[string]interface{}{
		"status":            "completed",
		"chunk_count":       len(chunks),
		"last_processed_at": time.Now(),
	})

	logger.Logger.Info("Document processed successfully", zap.Uint("doc_id", doc.ID))
}

// chunkDocument 文档分块
func (rs *RAGService) chunkDocument(content string, chunkSize int) ([]string, error) {
	var chunks []string
	runes := []rune(content)

	for i := 0; i < len(runes); i += chunkSize {
		end := i + chunkSize
		if end > len(runes) {
			end = len(runes)
		}
		chunks = append(chunks, string(runes[i:end]))
	}

	return chunks, nil
}

// countWords 计算字数
func (rs *RAGService) countWords(text string) int {
	return len([]rune(text)) // 简化实现，按字符计算
}

// SemanticSearch 语义搜索
func (rs *RAGService) SemanticSearch(ctx context.Context, req *SearchRequest) (*SearchResponse, error) {
	return rs.searchService.SemanticSearch(ctx, req)
}

// HybridSearch 混合搜索
func (rs *RAGService) HybridSearch(ctx context.Context, req *SearchRequest) (*SearchResponse, error) {
	return rs.searchService.HybridSearch(ctx, req)
}

// AskQuestion 问答
func (rs *RAGService) AskQuestion(ctx context.Context, req *QARequest) (*QAResponse, error) {
	return rs.qaService.AskQuestion(ctx, req)
}

// ChatWithKnowledge 知识问答对话
func (rs *RAGService) ChatWithKnowledge(ctx context.Context, req *ChatRequest) (*ChatResponse, error) {
	return rs.qaService.ChatWithKnowledge(ctx, req)
}
